﻿//TODO: Thinning: TEST

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// Makes lines thinner.
    /// </summary>
    public class Thinning : BaseImageFilter
    {
        private int m_nIterations = 1;
        private int[] m_aKernel = new int[] { 1, 1, 1, 1, 1, 1, 1, 1, 1 };

        /// <summary>
        /// default constructor
        /// </summary>
        public Thinning()
        {
        }

        /// <summary>
        /// primary constructor specify kernel and iterations directly
        /// </summary>
        /// <param name="_aKernel"></param>
        /// <param name="_nIterations"></param>
        public Thinning(int[] _aKernel, int _nIterations)
        {
            this.m_aKernel = _aKernel;
            this.m_nIterations = _nIterations;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="_thinning"></param>
        internal Thinning(Thinning _thinning)
        {
            this.m_aKernel = _thinning.m_aKernel;
            this.m_nIterations = _thinning.m_nIterations;
        }

        /// <summary>
        /// Executes this filter
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            IEnumerator it = null;

            HitAndMiss HitMiss = new HitAndMiss();
            Point point = Point.Empty;
            HashSet result = new HashSet();
            HashSet inputHashSet = new HashSet();

            UnsafeBitmap bitmap = _bitmap;

            int nHeight = bitmap.Height;
            int nWidth = bitmap.Width;

            for (int n = 0; n < m_nIterations; n++)
            {
                if (HitMiss.KernelNo0s(m_aKernel))
                {
                    for (int j = 0; j < nHeight; ++j)
                    {
                        for (int i = 0; i < nWidth; ++i)
                        {
                            if (bitmap[i, j] == Definitions.White)
                            {
                                inputHashSet.Add(new Point(i, j));
                            }
                        }
                    }
                }
                else
                {
                    it = bitmap.m_ForegroundEdgePixels.GetEnumerator();
                    while (it.MoveNext())
                    {
                        inputHashSet.Add(it.Current);
                    }
                }
                result = HitMiss.HitMissHashSet(bitmap, inputHashSet, m_aKernel);
                it = result.GetEnumerator();
                while (it.MoveNext())
                {
                    point = (Point)it.Current;
                    //make p a background pixel and update the edge sets
                    bitmap.RemovePixel(point);
                    bitmap.m_ForegroundEdgePixels.Remove(point);
                    bitmap.m_BackgroundEdgePixels.Add(point);
                    //check if new foreground pixels are exposed as edges
                    for (int j = -1; j < 2; ++j)
                    {
                        for (int k = -1; k < 2; ++k)
                        {
                            if (point.X + j >= 0 && point.Y + k > 0 &&
                               point.X + j < nWidth && point.Y + k < nHeight &&
                               bitmap[point.X + j, point.Y + k] == Definitions.White)
                            {
                                Point p2 = new Point(point.X + j, point.Y + k);
                                bitmap.m_ForegroundEdgePixels.Add(p2);
                            }
                        }
                    }
                }
            }

            foreach (Point pt in bitmap.m_ForegroundEdgePixels)
            {
                bitmap.SetPixel(pt.X, pt.Y, Definitions.White);
            }
            foreach(Point pt in bitmap.m_BackgroundEdgePixels)
            {
                bitmap.SetPixel(pt.X, pt.Y, Definitions.Black);
            }
            return bitmap;
        }

        /// <summary>
        /// used to create a deep copy
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new Thinning(this);
        }
    }
}
